/* 2. Promise 有三个状态: fulfilled(成功), rejected(失败), pending(等待)
 *    pending → fulfilled
 *    pending → reject
 *    一旦状态改变后就不可更改 */
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  /*1. Promise 是一个类, 声明的时候传入函数会立刻执行,
       这个函数有两个形参 resolve 和 reject 用来改变promise的状态
	     为了代码的严谨, 捕获执行时需要捕获异常 */
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  // 状态: 最初设置为pending
  status = PENDING;
  // 成功的返回值
  value = undefined;
  // 失败的返回值
  reason = undefined;
  // 成功的回调
  successCallback = [];
  // 失败的回调
  failCallback = [];

  /* 3. resolve 和 reject 用来改变状态
   *    resolve → fulfilled
   *    reject → rejected*/
  resolve = (value) => {
    // 如果不是pending才改变状态
    if (this.status !== PENDING) return;
    // 将状态改成成功
    this.status = FULFILLED;
    // 下一个 then 的实参
    this.value = value;
    while (this.successCallback.length > 0) this.successCallback.shift()();
  };

  reject = (reason) => {
    // 如果不是pending才改变状态
    if (this.status !== PENDING) return;
    // 将状态改成失败
    this.status = REJECTED;
    this.reason = reason;
    while (this.failCallback.length) this.failCallback.shift()();
  };

  /*4. then 方法做的事就是判断状态, 如果状态是成功, 调用成功的回调, 反之调用失败的方法
		   成功时需要将成功的数据 value 作为参数传给成功的回调函数
		   失败时需要将失败的数据 reason 作为参数传给失败的回调函数 */
  then(successCallback, failCallback) {
    successCallback = successCallback ? successCallback : (value) => value;
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 使用 setTimeout 是为了确保 resolvePromise 能取得 promise 对象
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断 x 是普通值还是 promise 对象
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 是普通值还是 promise 对象
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // pending 时将状态存储起来
        this.successCallback.push(() => {
          // 模拟微任务
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              // 判断 x 是普通值还是 promise 对象
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              // 判断 x 是普通值还是 promise 对象
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  /* 5. 实现 all 方法
   *    定义成静态方法是因为 all 可以用类名直接调用: MyPromise.all
   *    普通的方法需要用实例调用: new MyPromise().then
   *    接收数组作为参数, 数组可以是
   *      普通值(传入时是普通值则返回的结果也是普通值)
   *      promise(传入时是promise对象则返回的结果是promise的值)
   *    调用成功后返回 promise 对象
   */
  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(
            (value) => addData(i, value),
            (reason) => reject(reason)
          );
        } else {
          // 普通值
          addData(i, array[i]);
        }
      }
    });
  }

  /* 6. 实现 resolve 方法
   *    接收参数
   *      普通值(传入时是普通值则返回的结果也是普通值)
   *      promise(传入时是promise对象则返回promise对象)
   * */
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  /* 7. 实现 finally 方法
   *    接收一个回调函数为参数，无论 Promise 是否成功, 都会执行
   *    返回一个 then 方法, 可以拿到当前 promise 对象的结果
   * */
  finally(callback) {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) =>
        MyPromise.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }

  /* 8. 实现 catch 方法
   *    当 promise 的 then 没有捕获失败情况的时候, 会将错误传递给 catch */
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
}

// 判断 x 是普通值还是 promise 对象,
// 如果是 promise 对象的的话查看 promise 返回的结果, 再根据结果决定调用 resolve 还是 promise
// 如果是普通值 直接调用 resolve
function resolvePromise(promise2, x, resolve, reject) {
  // 如果自己返回自己, 则报错
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

module.exports = MyPromise;

// 测试运行 promises-aplus-tests ./MyPromise.js
// 太难通过了 不强求(T＿T)
// MyPromise.deferred = function () {
//   const defer = {};
//   defer.promise = new MyPromise((resolve, reject) => {
//     defer.resolve = resolve;
//     defer.reject = reject;
//   });
//   return defer;
// };
